/**
 * description: 绘制贝塞尔曲线 需要支持深度拾取、地形深度探测
 * fileName: CurveUtils.ts 
 * author: ssr 
 * date: 2023-01-13 16:16:06
 * version: V1.0.0 
 */
import { Cartesian3, Viewer, Math as CesiumMath } from "cesium";

interface Point {
  x: number;
  y: number;
}
export default class CurveUtils {

  /**
   * 贝塞尔曲线实现
   * @param positions
   * @returns
   */
  static fineBezier(viewer: Viewer, positions: Cartesian3[]) {
    //返回贝塞尔曲线各点数据数组
    var pointNUM = 40; //个点 呼叫者必须分配足夠的记忆点以供输出结果
    var poins2D: Point[] = [];
    var d: number[] = [];
    for (var i = 0; i < positions.length; i++) {
      // 经纬度数据
      var res = CurveUtils.car3ToLatLog(viewer, positions[i]);
      var point: Point = {
        x: res[0],
        y: res[1],
      };
      poins2D.push(point); //数组添加point对象
    }
    var cbs = CurveUtils.computeBezier(poins2D, pointNUM); //计算贝塞尔曲线

    for (var j = 0; j < cbs.length; j++) {
      d.push(cbs[j].x);
      d.push(cbs[j].y);
    }
    return Cartesian3.fromDegreesArray(d); //把贝塞尔曲线返回的经纬度数据存到新数组，转换成世界坐标
  }

  /**
   * 世界坐标转换成经纬度坐标，返回一组经纬度数组
   */
  static car3ToLatLog(viewer: Viewer, cartesian: Cartesian3) {
    var latlng =
      viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian);
    var lat = CesiumMath.toDegrees(latlng.latitude);
    var lng = CesiumMath.toDegrees(latlng.longitude);
    return [lng, lat];
  }

  /**
   * 转为曲线坐标
   * @param cp 坐标点
   * @param numberOfPoints 插值数量
   * @returns 
   */
  static computeBezier(cp: Point[], numberOfPoints: number): Cartesian3[] {
    //点的经纬度数组，点数
    var dt: number;
    var i: number;
    var curve: number[] = [];
    dt = 1.0 / (numberOfPoints - 1);
    for (i = 0; i < numberOfPoints; i++) {
      let point = CurveUtils.PointOnCubicBezier(cp, i * dt); //曲线上的每个记忆点都对应一个t t的范围在[0-1]
      curve.push(point.x);
      curve.push(point.y);
    }
    return Cartesian3.fromDegreesArray(curve);
  }

  /**
   * @param cp cp在此是四个元素的阵列:
   * cp[0]为起始点，或上图中的P0
   * cp[1]为第一个控制点，或上图中的P1
   * cp[2]为第二个控制点，或上图中的P2
   * cp[3]为结束点，或上图中的P3
   * @param t t为参數值，0 <= t <= 1
   * @returns 
   */
  static PointOnCubicBezier(cp: Point[], t: number): Point {
    var ax: number, bx: number, cx: number;
    var ay: number, by: number, cy: number;
    var tSquared: number, tCubed: number;
    var length = cp.length;
    var inteval = Math.floor(length / 4); // 向下取整 找下标
    /*计算多项式系数  三次贝塞尔的计算公式*/
    cx = 3.0 * (cp[inteval].x - cp[0].x);
    bx = 3.0 * (cp[2 * inteval].x - cp[inteval].x) - cx;
    ax = cp[length - 1].x - cp[0].x - cx - bx;
    cy = 3.0 * (cp[inteval].y - cp[0].y);
    by = 3.0 * (cp[2 * inteval].y - cp[inteval].y) - cy;
    ay = cp[length - 1].y - cp[0].y - cy - by;
    /*计算位于参数值t的曲线点*/
    tSquared = t * t; //t^2
    tCubed = tSquared * t; //t^3
    let result: Point = {
      x: ax * tCubed + bx * tSquared + cx * t + cp[0].x,
      y: ay * tCubed + by * tSquared + cy * t + cp[0].y,
    };
    return result;
  }
}
